package com.fyt.reachgo.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fyt.reachgo.domain.City;
import com.fyt.reachgo.domain.Result;
import com.fyt.reachgo.domain.VO.CityCallingCardVO;
import com.fyt.reachgo.domain.dto.CityCallingCardDTO;
import com.fyt.reachgo.service.CityService;
import com.fyt.reachgo.mapper.CityMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.fyt.reachgo.utils.RedisConstants.*;

/**
* @author 27675
* @description 针对表【city(城市基本信息及地理坐标表)】的数据库操作Service实现
* @createDate 2025-02-25 08:37:03
*/
@Service
public class CityServiceImpl extends ServiceImpl<CityMapper, City>
    implements CityService{

    @Autowired
    private CityMapper cityMapper;

    private final StringRedisTemplate redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public CityServiceImpl(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public Result<List<CityCallingCardVO>> getCityList(CityCallingCardDTO cityCallingCardDTO) {
        // 1 查询缓存
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(CACHE_CITY_KEY + cityCallingCardDTO.getProvinceId()))){
            String json = stringRedisTemplate.opsForValue().get(CACHE_CITY_KEY + cityCallingCardDTO.getProvinceId());
            if (StrUtil.isBlank(json)){
                return Result.error("省份不存在");
            }
            List<CityCallingCardVO> cityList = JSONUtil.toList(json, CityCallingCardVO.class);
            return Result.success(cityList);
        }
        // 2. 缓存为空，查询数据库
        List<CityCallingCardVO> cityList = cityMapper.getCityList(cityCallingCardDTO);
        // 数据库不存在
        if (cityList.isEmpty()){
            redisTemplate.opsForValue().set(CACHE_CITY_KEY + cityCallingCardDTO.getProvinceId(), "");
            redisTemplate.expire(CACHE_CITY_KEY + cityCallingCardDTO.getProvinceId(), CACHE_NULL_TTL, TimeUnit.MINUTES);
            return Result.error("省份不存在");
        }
        // 3. 序列化为JSON并存入缓存（使用String类型）
        String jsonStr = JSONUtil.toJsonStr(cityList);
        redisTemplate.opsForValue().set(CACHE_CITY_KEY, jsonStr);
        redisTemplate.expire(CACHE_CITY_KEY, CACHE_CITY_TTL, TimeUnit.MINUTES);
        return Result.success(cityList);
    }

    /**
     * 根据城市ID获取城市信息,并解决穿透，雪崩，击穿问题
     * @param cityCallingCardDTO
     * @return
     */
    @Override
    public Result<City> getCityById(CityCallingCardDTO cityCallingCardDTO) {
        String cityKey = CACHE_CITY_KEY + cityCallingCardDTO.getCityId();// 创建每个城市唯一key

        // 1. 查询缓存
        String cacheJson = redisTemplate.opsForValue().get(cityKey);
        if (cacheJson != null) {
            // 空值处理（缓存穿透保护）
            if (cacheJson.isEmpty()) {
                return Result.error("数据不存在");
            }
            // 反序列化缓存数据
            return Result.success(JSONUtil.toBean(cacheJson, City.class));
        }

        // 2. 缓存未命中，尝试获取分布式锁
        String lockKey = cityKey + ":LOCK";
        boolean locked = false;
        try {
            // 尝试获取锁（原子操作）
            // 锁过期时间防止死锁
            locked = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(
                    lockKey,
                    "locked",
                    10, // 锁过期时间防止死锁
                    TimeUnit.SECONDS
            ));

            if (locked) {
                // 二次检查缓存（其他线程可能已写入）
                String doubleCheckJson = redisTemplate.opsForValue().get(cityKey);
                if (doubleCheckJson != null) {
                    return Result.success(JSONUtil.toBean(doubleCheckJson, City.class));
                }

                // 查询数据库
                City city = getById(cityCallingCardDTO.getCityId());

                // 处理缓存穿透
                if (city == null) {
                    redisTemplate.opsForValue().set(
                            cityKey,
                            "",
                            CACHE_NULL_TTL, // 空值过期时间（建议30秒）
                            TimeUnit.SECONDS
                    );
                    return Result.error("数据不存在");
                }

                // 写入缓存（解决雪崩：基础时间+随机值）
                long expireTime = CACHE_CITY_ONE_TTL + RandomUtil.randomLong(0, 120);
                redisTemplate.opsForValue().set(
                        cityKey,
                        JSONUtil.toJsonStr(city),
                        expireTime,
                        TimeUnit.SECONDS
                );

                return Result.success(city);
            } else {
                // 未获取到锁，短暂等待后重试
                Thread.sleep(50);// 线程休眠50ms
                return getCityById(cityCallingCardDTO); // 递归重试
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return Result.error("系统繁忙，请稍后重试");
        } finally {
            if (locked) {
                redisTemplate.delete(lockKey); // 释放锁
            }
        }
    }




}




















